//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_PAGING_H__
#define __ELASTOS_PAGING_H__

#include <regs.h>

// Page macros
#define PAGE_SHIFT                  (12)
#define PAGEDIR_SHIFT               (22)
#define PAGE_TO_PAGEDIR_SHIFT       (PAGEDIR_SHIFT - PAGE_SHIFT)

#define PAGE_SIZE                   (1u << PAGE_SHIFT)
#define PAGEDIR_SIZE                (1u << PAGEDIR_SHIFT)

#define PAGE_MASK                   (~(PAGE_SIZE - 1))
#define PAGEDIR_MASK                0xffc00000
#define PAGETAB_MASK                0xfffff000

#define PAGE_OFFSET(va)             ((uint32_t)(va) & (PAGE_SIZE - 1))
#define PAGETAB_INDEX(va)           (((uint32_t)(va) >> PAGE_SHIFT) & 0x3ff)
#define PAGEDIR_INDEX(va)           ((uint32_t)(va) >> PAGEDIR_SHIFT)

// Convertion
#define NUMBEROF_PAGES(size)        ((size) >> PAGE_SHIFT)
#define PHYSADDR_TO_PAGENO(pa)      ((pa) >> PAGE_SHIFT)
#define VIRTADDR_TO_PAGENO(va)      PHYSADDR_TO_PAGENO(PHYSADDR(va))
#define PAGENO_TO_PHYSADDR(no)      ((no) << PAGE_SHIFT)
#define PAGENO_TO_VIRTADDR(no)      VIRTADDR(PAGENO_TO_PHYSADDR(no))

#define NUMBEROF_PAGETABS(size)     ((size) >> PAGEDIR_SHIFT)
#define PHYSADDR_TO_PAGETABNO(pa)   ((pa) >> PAGEDIR_SHIFT)
#define VIRTADDR_TO_PAGETABNO(va)   PHYSADDR_TO_PAGETABNO(PHYSADDR(va))
#define PAGETABNO_TO_PHYSADDR(no)   ((no) << PAGEDIR_SHIFT)
#define PAGETABNO_TO_VIRTADDR(no)   VIRTADDR(PAGETABNO_TO_PHYSADDR(no))
#define VIRTPAGENO_TO_VIRTADDR(no)  ((no) << PAGE_SHIFT)

#define PAGENO_TO_PAGETABNO(no)     ((no) >> PAGE_TO_PAGEDIR_SHIFT)

const uint_t c_uMaxNumberOfPages    = NUMBEROF_PAGES(0xffffffff) + 1;

#define IS_VALID_PAGENO(no)         ((no) < c_uMaxNumberOfPages)

const uint_t c_uKernelBasePageNo    =
        PHYSADDR_TO_PAGENO((physaddr_t)KERNEL_BASE);

const uint_t c_uNumberOfKernelPages =
        c_uMaxNumberOfPages - c_uKernelBasePageNo;

const uint_t c_uKernelPhysBasePageNo =
        PHYSADDR_TO_PAGENO(bsp.paKernelPhysBase);

#define PHYSPAGENO_TO_VIRTPAGENO(no) \
        ((no) + c_uKernelBasePageNo - c_uKernelPhysBasePageNo)

#define VIRTPAGENO_TO_PHYSPAGENO(no) \
        ((no) - c_uKernelBasePageNo + c_uKernelPhysBasePageNo)

typedef struct PageDirEntry {
    uint32_t uValue;
} PageDirEntry;

typedef struct PageTabEntry {
    uint32_t uValue;
} PageTabEntry;

// because the vaddress 10 : 10 : 12, so need 2^(10 + sizeof(PageDirEntry))

const size_t c_uSizeOfPageDir           = 0x1000;
const size_t c_uSizeOfPageTab           = 0x1000;

const uint_t c_uNumberOfPageDirEntries  =
        c_uSizeOfPageDir / sizeof(PageDirEntry);
const uint_t c_uNumberOfPageTabEntries  =
        c_uSizeOfPageTab / sizeof(PageTabEntry);

const uint_t c_cPageDirEntries = c_uNumberOfPageDirEntries;
const uint_t c_cPageTabEntries = c_uNumberOfPageTabEntries;

EXTERN PageDirEntry g_initPageDir[c_uNumberOfPageDirEntries];

#define PageEntryFlag_Global        (0x1 << 0)  // Global page
#define PageEntryFlag_Valid         (0x1 << 1)  // Valid
#define PageEntryFlag_Writable      (0x1 << 2)  // Writable
                                                // (otherwise 0 indicates read only)
#define PageEntryFlag_NotExe        (0x1 << 30) // Cannot be execute
#define PageEntryFlag_Exe           0xbfffffff  // execute

#define PageEntryFlag_Uncached             (0x2 << 3)  // Uncached
#define PageEntryFlag_CacheableNoncoherent (0x3 << 3)
                                                // Cacheable Noncoherent
#define PageEntryFlag_CacheableCoherentExclusive        (0x4 << 3)
                                                // Cacheable Coherent Exclusive
#define PageEntryFlag_CacheableCoherentExclusiveOnWrite (0x5 << 3)
                                    // Cacheable Coherent Exclusive on Write
#define PageEntryFlag_CacheableCoherentUpdateOnWrite    (0x6 << 3)
                                    // Cacheable Coherent Update on Write

INLINE void EnableMMU(uint32_t cr3, physaddr_t pagedir, uint32_t cr1)
{
}

INLINE void SetPageDirAddress(uint32_t uASID)
{
#if defined(_GNUC)
    ASM("mtc0 %0, $10;"::"r"(uASID));  // set the EntryHi
#elif defined(_EVC)
    ASM("mtc0 %0, $10;", uASID);  // set the EntryHi
#else
#error unknown compiler
#endif
}

INLINE uint32_t GetFaultStatus()
{
    uint32_t fs;

//    ASM("mrc    p15, 0, %0, c5, c0, 0;"
//        :"=r"(fs));
    ASM("break 0;");

    return fs;
}

INLINE virtaddr_t GetFaultAddress()
{
    virtaddr_t fa;

//    ASM("mrc    p15, 0, %0, c6, c0, 0;"
//        :"=r"(fa));
    ASM("break 0;");

    return fa;
}

INLINE void FlushInstructionCache(uint_t uBase, uint_t uLimit)
{
    // BUGBUG: Need to implement?
}

#endif //__ELASTOS_PAGING_H__
